 /************************************************************************/
/* particleEmitter.cpp                                                  */
/* 2004/4/30                                                            */
/************************************************************************/
#include        <string>
#include        <d3dx9.h>
#include        <stdexcept>
#include        <vector>
#include        <windows.h>
#include        <math.h>
#include        <io.h>
#include        <fcntl.h>
#include        "Funcs.h"
#include        "Particle.h"
#include        "RecycleArray.h"
#include        "EventList.h"
#include        "Particle.h"
#include        "EffectDataStruct.h"
#include        "ParticleEmitter.h"
/************************************************************************/
#include        "FXRenderState.h"
#include        "DXTManager.h"
/************************************************************************/

#define DEG_TO_RAD(X) ((X) * 3.14 / 180)

//extern bool g_Effect;

/************************************************************************/
/* ?                                                                    */
/************************************************************************/
void CParticleEmitter::SetFrameProps(
    float fFrameSpeed, float fTicksPerFrame, float fLastFrame)
{
    m_fFrameSpeed = fFrameSpeed;
    m_fTicksPerFrame = fTicksPerFrame;
    m_fLastFrame = fLastFrame;
}


/************************************************************************/
/* ?                                                                    */
/************************************************************************/
HRESULT CParticleEmitter::RestoreDeviceObjects(
    LPDIRECT3DDEVICE9 pDev, VertexBufferItem* pVB)
{
    m_pd3dDevice = pDev;
    m_vbParticles = pVB;
    return S_OK;	
}

/************************************************************************/
/* ?                                                                    */
/************************************************************************/
HRESULT CParticleEmitter::LoadObjects()
{
    SAFE_RELEASE(m_texParticle);
    
    FX_UTIL::CreateCompressedTexture(m_pd3dDevice,
        m_szTextureName, m_texParticle, true);
	if( m_texParticle ) m_texParticle->loadResource(m_szTextureName);
    return S_OK;
}

/************************************************************************/
/* ?                                                                    */
/************************************************************************/
void CParticleEmitter::InvalidateDeviceObjects()
{
    SAFE_RELEASE(m_texParticle);
}

/************************************************************************/
/* ?                                                                    */
/************************************************************************/
void CParticleEmitter::RenewParts(
        CPART_DATA& partData, const float& fElapsed)
{
    for (int q = 0; q < partData.GetParts()->GetTotalElements(); ++q)
    {
        if (partData.GetParts()->IsAlive(q))
        {
			CParticle& particle = partData.GetParts()->GetAt( q );
            //CParticle& particle = (*(partData.GetParts()))[q];
    	    D3DXCOLOR Color = GetColor(particle.m_fAge);
            particle.m_Color = Color;

            if (!(particle.Update( fElapsed))) 
            {
                partData.GetParts()->Delete( &particle );
            }
        }
    }     
    return;
}

/************************************************************************/
/* ?                                                                    */
/************************************************************************/
void CParticleEmitter::CalcPartTransform(CPART_DATA& partData,
        const int& nNumNewParts)
{
    for (int i = 0; i < nNumNewParts; ++i)
    {
        CParticle* pPart = partData.GetParts()->New();
        if( pPart == NULL ) { return; }
		pPart->m_fLifetime = m_fLifeTime;
		pPart->m_vPos.x = RandomNumber(m_vPos1.x, m_vPos2.x);
		pPart->m_vPos.z = RandomNumber(m_vPos1.z, m_vPos2.z);
        pPart->m_vPos.y = RandomNumber(m_vPos1.y, m_vPos2.y);

		pPart->m_fGravity = m_fGravity;
		
	D3DXVec3TransformCoord(&(pPart->m_vPos),
            &(pPart->m_vPos), &partData.GetMatTransform());
        if (!m_bTail)
        {
			if(!m_bSphere)
			{
				pPart->m_vDir.x = 
					RandomNumber( m_vSpawnDir1.x, m_vSpawnDir2.x );
				pPart->m_vDir.z = 
					RandomNumber( m_vSpawnDir1.z, m_vSpawnDir2.z );
				pPart->m_vDir.y = 
					RandomNumber( m_vSpawnDir1.y, m_vSpawnDir2.y );
			}
			else
			{
				for(;;)
				{
					pPart->m_vDir.x = 
						RandomNumber( -m_fRadius, m_fRadius );
					pPart->m_vDir.z = 
						RandomNumber( -m_fRadius, m_fRadius );
					pPart->m_vDir.y = 
						RandomNumber( -m_fRadius, m_fRadius );
					if(D3DXVec3Length(&(pPart->m_vDir)) < m_fRadius)
						break;
				}
			}

            D3DXMATRIX matRot = partData.GetMatTransform();
            matRot._41  = 0.0f;
            matRot._42  = 0.0f;
            matRot._43  = 0.0f;

            D3DXVec3TransformCoord(&(pPart->m_vDir),
                &(pPart->m_vDir), &matRot);            
        }
        else
        {
            if (partData.GetIsFirstUpdate())
            {
                partData.SetOldPos(D3DXVECTOR3( partData.GetMatTransform()._41,
                    partData.GetMatTransform()._42,
                    partData.GetMatTransform()._43));
                partData.TurnFirstUpdateOff();
            }
            pPart->m_vDir.x = 
                partData.GetOldPos().x - partData.GetMatTransform()._41;

            pPart->m_vDir.y = 
                partData.GetOldPos().y - partData.GetMatTransform()._42;

            pPart->m_vDir.z = 
                partData.GetOldPos().z - partData.GetMatTransform()._43;
        }
    } 
    return;
}

/************************************************************************/
/*                                                                      */
/************************************************************************/
void CParticleEmitter::GenNewParts(CPART_DATA& partData,
        const float& fElapsed)
{
    if (!partData.GetIsOn()) return;   

    float fEmitRateThisFrame = RandomNumber(m_fMinEmitRate, m_fMaxEmitRate);
    int nNumNewParts = fEmitRateThisFrame * fElapsed;
    m_fNumNewPartsExcess +=
	             (float)(fEmitRateThisFrame * fElapsed) - nNumNewParts;

    if (m_fNumNewPartsExcess > 1.0f)
    {
        nNumNewParts += (int)m_fNumNewPartsExcess;
        m_fNumNewPartsExcess -= (int)m_fNumNewPartsExcess;
    }
    CalcPartTransform(partData, nNumNewParts);
    return;
}

/************************************************************************/
/*                                                                      */
/************************************************************************/
bool CParticleEmitter::Update(CPART_DATA& partData, const float& fElapsed)
{
    partData.SetCurFrame(partData.GetCurFrame() + 
        fElapsed * m_fTicksPerFrame * m_fFrameSpeed);

    if (partData.GetCurFrame() >= m_fEndTime)
    {
        if (partData.GetIsLooped()) partData.ResetCurFrame(); 
        else partData.TurnOff(); 
    }

    RenewParts(partData, fElapsed);
    if (!partData.GetIsOn()) return false;

    GenNewParts(partData, fElapsed);

    partData.SetOldPos(D3DXVECTOR3(partData.GetMatTransform()._41,
        partData.GetMatTransform()._42, partData.GetMatTransform()._43));
    return false;
}


/************************************************************************/
/*                                                                      */
/************************************************************************/
void
CParticleEmitter::DrawParticles( CPART_DATA& partData )
{
/*    m_pd3dDevice->SetStreamSource(0, m_vbParticles,
        0, sizeof(FX_COMMON_VERTEX));*/
    m_vbParticles->activate(0,0,0);

    m_pd3dDevice->SetFVF(D3DFVF_FX_COMMON_VERTEX) ;
    m_pd3dDevice->SetVertexShader(NULL);
    if (m_texParticle)m_pd3dDevice->SetTexture(0, m_texParticle->getTexture());
    
    FX_COMMON_VERTEX *pVertices;
    DWORD dwNumParticlesToRender = 0;

    pVertices=(FX_COMMON_VERTEX*)m_vbParticles->lock(nWriteLock, 0,0);
   
    for (int q = 0; q < NUM_PARTICLES; ++q)
    {
        if (partData.GetParts()->IsAlive(q))
        {
           const CParticle& part = partData.GetParts()->GetAt( q );

           pVertices->vPos = part.m_vPos;
           pVertices->dwColor = (DWORD)part.m_Color;
           pVertices++;
           
           if (++dwNumParticlesToRender == NUM_PARTICLES)
           {
               m_vbParticles->unlock();
               m_pd3dDevice->DrawPrimitive(D3DPT_POINTLIST, 0, 
                                           dwNumParticlesToRender);

               pVertices=(FX_COMMON_VERTEX *)m_vbParticles->lock(nWriteLock,0,0);
               dwNumParticlesToRender = 0;
           }
       }
   } 
   m_vbParticles->unlock(); 
   
   if (dwNumParticlesToRender)
   {
       m_pd3dDevice->DrawPrimitive(D3DPT_POINTLIST, 0, dwNumParticlesToRender);
   }
}

/************************************************************************/
/*                                                                      */
/************************************************************************/
void CParticleEmitter::Render(CPART_DATA& partData)
{
//	if(!g_Effect)	return;
    if (!partData.GetIsOn()) return;
    SetRenderState();  
    m_pd3dDevice->SetTransform(D3DTS_WORLD, &m_matIdentity);
    DrawParticles(partData);
    m_pd3dDevice->SetTransform(D3DTS_WORLD, &m_matIdentity);
    m_pd3dDevice->SetTexture(0, NULL);
}

/************************************************************************/
/*                                                                      */
/************************************************************************/
void CParticleEmitter::SetRenderState(void)
{
    m_pd3dDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    m_pd3dDevice->SetRenderState(D3DRS_FOGENABLE, FALSE);
    m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
    m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
    m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

    m_pd3dDevice->SetRenderState(D3DRS_ZENABLE, TRUE);
    m_pd3dDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);

    m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
    m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
    m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
    m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_TEXTURE);
    m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_DIFFUSE);
    m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_TEXTURE);
    m_pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
    m_pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE);

    m_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
    m_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); 

    m_pd3dDevice->SetRenderState(D3DRS_POINTSPRITEENABLE, TRUE);
    m_pd3dDevice->SetRenderState(D3DRS_POINTSCALEENABLE,  TRUE);
    m_pd3dDevice->SetRenderState(D3DRS_POINTSIZE,     FtoDW(m_fSize));
    m_pd3dDevice->SetRenderState(D3DRS_POINTSIZE_MIN, FtoDW(0.0f));
    m_pd3dDevice->SetRenderState(D3DRS_POINTSIZE_MAX, FtoDW(64.00f));
    m_pd3dDevice->SetRenderState(D3DRS_POINTSCALE_A,  FtoDW(0.00f));
    m_pd3dDevice->SetRenderState(D3DRS_POINTSCALE_B,  FtoDW(0.00f));
    m_pd3dDevice->SetRenderState(D3DRS_POINTSCALE_C,  FtoDW(1.0f));
}

/************************************************************************/
/*                                                                      */
/************************************************************************/
void CParticleEmitter::DecodeEventScript(const char* strEventScript)
{
	int nLineCount = 0;
	char strOutputString[256];
	char strRead[256];
	char strLine[256];
	char strGarbage[128];

	FILE* pStream = NULL;
	pStream = fopen(strEventScript, "rt");
	if(pStream == NULL)
	{
		sprintf(strOutputString, "Opend Failed: %s \n", strEventScript);
		OutputDebugString(strOutputString);
		return;
	}

	
	fgets(strLine, 256, pStream);
	sscanf(strLine, "%s", strRead);
	nLineCount++;
	
	if(_stricmp(strRead, "PARTICLE:") != 0)
	{
		sprintf(strOutputString, "Invalid data: %s:%d", strEventScript,
			                                            nLineCount);
		return;
	}

	fgets(strLine, 256, pStream);
	sscanf(strLine, "%s", strRead);
	nLineCount++;
	if(_stricmp(strRead, "BEGIN") != 0)
	{
		sprintf(strOutputString, "Invalid data: %s", strEventScript, 
			                                         nLineCount);
		return;
	}
    
	while(!feof(pStream))
	{
		++nLineCount;
		fgets(strLine, 256, pStream);
		if(_stricmp(strLine, "\n") == 0) continue;
		sscanf(strLine, "%s", strRead);

		if(_stricmp(strRead, "END") == 0)
		{
			LoadObjects();
			fclose(pStream);
			return;
		}
		else if(_stricmp(strRead, "NAME") == 0)
		{
			continue;
		}
		else if(_stricmp(strRead, "FRAMESPEED") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fFrameSpeed);
		}
		else if(_stricmp(strRead, "TICKSPERFRAME") == 0)
		{                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
			sscanf(strLine, "%s%f", strGarbage, &m_fTicksPerFrame);
		}
		else if(_stricmp(strRead, "LASTFRAME") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fLastFrame);
		}
		else if(_stricmp(strRead, "GRAVITY") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fGravity);
		}
		else if(_stricmp(strRead, "RADIUS") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fRadius);
			m_bSphere = TRUE;
		}
		else if(_stricmp(strRead, "EMITPOS") == 0)
		{
			sscanf(strLine, "%s%f%f%f%f%f%f", strGarbage,
													&m_vPos1.x,
													&m_vPos1.y,
													&m_vPos1.z,
													&m_vPos2.x,
													&m_vPos2.y,
													&m_vPos2.z);
		}
		else if(_stricmp(strRead, "TAIL") == 0)
		{
			sscanf(strLine, "%s%s", strGarbage, strGarbage);
			if(_stricmp(strGarbage, "TRUE") == 0) m_bTail = true;
			else if(_stricmp(strGarbage, "FALSE") == 0) m_bTail = false;
		}			
		else if(_stricmp(strRead, "ENDTIME") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fEndTime);
		}
		else if(_stricmp(strRead, "SIZE") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fSize);
		}
		else if(_stricmp(strRead, "EMITRATE") == 0)
		{
			float fEmitRate1, fEmitRate2;
			
			sscanf(strLine, "%s%f%f", strGarbage,&fEmitRate1, &fEmitRate2);
			SetEmitRate(fEmitRate1, fEmitRate2);
		}
		else if(_stricmp(strRead, "SPAWNDIR") == 0)
		{
			D3DXVECTOR3 vSpawnDir1, vSpawnDir2;
			
			sscanf(strLine, "%s%f%f%f%f%f%f", strGarbage, &vSpawnDir1.x,
				                                    &vSpawnDir1.y,
													&vSpawnDir1.z,
													&vSpawnDir2.x,
													&vSpawnDir2.y,
													&vSpawnDir2.z);
			SetSpawnDir(vSpawnDir1, vSpawnDir2);

		}
		else if(_stricmp(strRead, "LOOP") == 0)
		{
		}
		else if(_stricmp(strRead, "TRANSLOCAL") == 0)
		{
			D3DXVECTOR3 vPos;
			sscanf(strLine, "%s%f%f%f", strGarbage, &vPos.x, &vPos.y, &vPos.z);
		}
		else if(_stricmp(strRead, "ADDCOLOR") == 0)
		{
			float fTickNum, r, g, b, a;
			sscanf(strLine, "%s%f%f%f%f%f", strGarbage, &fTickNum, &r, &g,
				                                                      &b, &a);
			AddColor(fTickNum, D3DXCOLOR(r, g, b, a));
		}
		else if(_stricmp(strRead, "TEXTURE") == 0)
		{
			sscanf(strLine, "%s%s", strGarbage, m_szTextureName);
		}
		else if(_stricmp(strRead, "LIFE") == 0)
		{
			sscanf(strLine, "%s%f", strGarbage, &m_fLifeTime);
		}
		else if(_stricmp(strRead, "SRCBLEND") == 0)
		{
          
		}
		else if(_stricmp(strRead, "DESTBLEND") == 0)
		{
        
		}
		else if(_stricmp(strRead, "COLOROP") == 0)
		{
         
		}
		else if(_stricmp(strRead, "COLORARG1") == 0)
		{
         
		}
		else if(_stricmp(strRead, "COLORARG2") == 0)
		{
        
		}
		else if(_stricmp(strRead, "ALPHAARG1") == 0)
		{
        
		}
		else if(_stricmp(strRead, "ALPHAARG2") == 0)
		{
           
		}
		else if(_stricmp(strRead, "ALPHAOP") == 0)
		{
           
		}
        else if( _stricmp( strRead, "ADDPOS") == 0 )
        {

        }
	else
	{
		sprintf(strOutputString, "Invalid data: %s, %d, \n",
			    strRead, nLineCount, strEventScript);
		OutputDebugString(strOutputString);
		fclose(pStream);
		return;
	}	
    }
	sprintf(strOutputString, "Unexpeted end of file: %d", nLineCount);
	OutputDebugString(strOutputString);
	fclose(pStream);
}

